package ongd.servicios.persistencia;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import ongd.negocio.gestion.Entidad;
import ongd.servicios.persistencia.exceptions.IllegalOrphanException;
import ongd.servicios.persistencia.exceptions.NonexistentEntityException;
import ongd.servicios.persistencia.exceptions.PreexistingEntityException;

/**
 * Clase abstracta que contiene los método que para realizar operaciones
 * sobre la base de datos.
 *
 * @author <a href="mailto:egi0002@alu.ubu.es">Elena García Iglesias</a>
 * @author <a href="mailto:cms0021@alu.ubu.es">Cristina Martínez Sanz</a>
 * @version 1.0 20120124
 *
 */
public abstract class AJPAControlador implements IJPAControlador {

    /**
     * Método que crea un objeto de tipo entidad en la base de datos.
     * @param entidad Objeto que crearemos 
     * @param et Booleano que indica si hay que iniciar la transacción
     * @throws PreexistingEntityException
     * @throws Exception 
     */
    @Override
    public abstract void create(Entidad entidad, boolean et) throws PreexistingEntityException, Exception;

    /**
     * Método que edita un objeto de tipo entidad en la base de datos.
     * @param entidad Entidad que editaremos
     * @param et Booleano que indica si hay que iniciar la transacción
     * @throws IllegalOrphanException 
     * @throws NonexistentEntityException
     * @throws Exception 
     */
    @Override
    public abstract void edit(Entidad entidad, boolean et) throws IllegalOrphanException, NonexistentEntityException, Exception;

    /**
     * Método que borra un objeto de tipo entidad de la base de datos.
     * @param clase Clase correpondiente al objeto a borrar
     * @param obj Clave primaria del objeto a borrar
     * @param et Booleano que indica si hay que iniciar la transaccion
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException 
     */
    @Override
    public abstract void destroy(Class clase, Object obj, boolean et) throws IllegalOrphanException, NonexistentEntityException;

    /**
     * Método que busca una entidad en la base de datos.
     * @param cls Class correspondiente al objeto a buscar
     * @param id Clave primaria del objeto a buscar
     * @return entidad
     */
    @Override
    public abstract Entidad findEntidad(Class cls, Object id);

    /**
     * Método que devuelve una lista de entidades en función de la clase 
     * pasada por parámetro.
     * @param clase Class correspondiente a los objetos que quiero que me 
     * devuelva
     * @param em EntityManager
     * @return lista
     */
    public List<? extends Entidad> findEntities(Class clase, EntityManager em) {
        return findEntities(clase, em, true, -1, -1);
    }

    /**
     * Método que devuelve una lista de entidades.
     * @param clase Class correspondiente a los objetos buscados
     * @param em EntitiManages
     * @param maxResults Número de resultados
     * @param firstResult Primer resultado
     * @return lista de entidades
     */
    public List<? extends Entidad> findEntities(Class clase, EntityManager em, int maxResults, int firstResult) {
        return findEntities(clase, em, false, maxResults, firstResult);
    }

    /**
     * Método que devuelve una lista de entidades.
     * @param clase Class correspondiente a los objetos buscados
     * @param em EntitiManager
     * @param all booleano que indica si quiere todos los resultados
     * @param maxResults Número de resultados
     * @param firstResult Primer resultado
     * @return 
     */
    private List<? extends Entidad> findEntities(Class clase, EntityManager em, boolean all, int maxResults, int firstResult) {
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(cq.from(clase));
            Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
//            em.close();
        }
    }

    /**
     * Método que devuelve el número de registros que hay de una determinada
     * entidad.
     * @param clase Class correpondiente a la entidad
     * @param em EntityManager
     * @return Numero de entidades
     */
    public int getEntidadCount(Class clase, EntityManager em) {
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<Class> rt = cq.from(clase);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
//            em.close();
        }
    }
}